package org.mysterylab.utopiaframework.util.web;

import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeMap;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.mysterylab.utopiaframework.util.security.EncoderUtil;
import org.springframework.util.Assert;

/**
 * HTTP 的工具类, 主要封装了 HTTP 中的 Header 等信息.
 * 
 * @author zhouych
 */
public class HttpUtil {

	//-- Header 的返回类型 --//
	
	public static final String HEADER_TYPE_TEXT	= "text/plain";
	
	public static final String HEADER_TYPE_JSON	= "application/json";
	
	public static final String HEADER_TYPE_XML	= "text/xml";
	
	public static final String HEADER_TYPE_HTML	= "text/html";
	
	public static final String HEADER_TYPE_JS	= "text/javascript";
	
	public static final String HEADER_TYPE_EXCEL= "application/vnd.ms-excel";

	/**
	 * 设置客户端缓存过期时间 的 Header.
	 * 
	 * @param response
	 *            服务端响应
	 * @param expiresSeconds
	 *            过期时间
	 */
	public static void setExpiresHeader(HttpServletResponse response,
			long expiresSeconds) {
		
		// Http 1.0 header
		response.setDateHeader("Expires", System.currentTimeMillis()
				+ expiresSeconds * 1000);
		// Http 1.1 header
		response.setHeader("Cache-Control", "private, max-age=" + expiresSeconds);
	}
	
	/**
	 * 设置禁止客户端缓存的 Header.
	 * 
	 * @param response
	 *            服务端响应
	 */
	public static void setDisableCacheHeader(HttpServletResponse response) {
		// Http 1.0 header
		response.setDateHeader("Expires", 1L);
		response.addHeader("Pragma", "no-cache");
		// Http 1.1 header
		response.setHeader("Cache-Control", "no-cache, no-store, max-age=0");
	}

	/**
	 * 设置 LastModified Header.
	 * 
	 * @param response
	 *            服务端响应
	 * @param lastModifiedDate
	 *            最后修改时间
	 */
	public static void setLastModifiedHeader(HttpServletResponse response,
			long lastModifiedDate) {
		response.setDateHeader("Last-Modified", lastModifiedDate);
	}

	/**
	 * 设置 Etag Header.
	 * 
	 * @param response
	 *            服务端响应
	 * @param etag
	 *            被请求变量的实体值
	 */
	public static void setEtag(HttpServletResponse response, String etag) {
		response.setHeader("ETag", etag);
	}
	
	/**
	 * 根据浏览器 If-Modified-Since Header, 计算文件是否已被修改. 如果无修改,
	 * checkIfModify 返回 false, 设置 304 not modify status.
	 * 
	 * @param lastModified
	 *            内容的最后修改时间
	 */
	public static boolean checkIfModifiedSince(HttpServletRequest request,
			HttpServletResponse response, long lastModified) {
		
		long ifModifiedSince = request.getDateHeader("If-Modified-Since");
		if ((ifModifiedSince != -1) && (lastModified < ifModifiedSince + 1000)) {
			response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
			return false;
		}
		return true;
	}
	
	/**
	 * 根据浏览器 If-None-Match Header, 计算 Etag 是否已无效. 如果
	 * Etag 有效, checkIfNoneMatch 返回 false, 设置 304 not modify status.
	 * 
	 * @param etag
	 *            被请求变量的实体值
	 */
	public static boolean checkIfNoneMatchEtag(HttpServletRequest request,
			HttpServletResponse response, String etag) {
		String headerValue = request.getHeader("If-None-Match");
		if (headerValue != null) {
			boolean conditionSatisfied = false;
			if (!"*".equals(headerValue)) {
				StringTokenizer commaTokenizer = new StringTokenizer(
						headerValue, ",");

				while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) {
					String currentToken = commaTokenizer.nextToken();
					if (currentToken.trim().equals(etag)) {
						conditionSatisfied = true;
					}
				}
			} else {
				conditionSatisfied = true;
			}

			if (conditionSatisfied) {
				response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
				response.setHeader("ETag", etag);
				return false;
			}
		}
		return true;
	}
	
	/**
	 * 设置让浏览器弹出下载对话框的 Header.
	 * 
	 * @param fileName
	 *            下载后的文件名
	 */
	public static void setFileDownloadHeader(HttpServletResponse response,
			String fileName) {
		try {
			// 中文文件名支持
			String encodedfileName = new String(fileName.getBytes(),
					"ISO8859-1");
			response.setHeader("Content-Disposition", "attachment; filename=\""
					+ encodedfileName + "\"");
		} catch (UnsupportedEncodingException e) {
			//
		}
	}

	/**
	 * 取得带相同前缀的 Request Parameters. 返回的结果的 Parameter 名已去除前缀.
	 * 
	 * @param request
	 *            客户端请求
	 * @param prefix
	 *            参数前缀
	 * @return
	 */
	public static Map<String, Object> getParametersStartsWith(ServletRequest request,
			String prefix) {
		
		Assert.notNull(request, "Request must not be null");
		// 获取参数集, 形如: [method, productid, pageid] 等
		Enumeration<String> paramNames = request.getParameterNames();
		Map<String, Object> params = new TreeMap<String, Object>();
		if (prefix == null) {
			prefix = "";
		}
		while (paramNames != null && paramNames.hasMoreElements()) {
			String paramName = paramNames.nextElement();
			if ("".equals(prefix) || paramName.startsWith(prefix)) {
				String unprefixed = paramName.substring(prefix.length());
				String[] values = request.getParameterValues(paramName);
				if (values == null || values.length == 0) {
					// do nothing
				} else if (values.length > 1) {
					params.put(unprefixed, values);
				} else {
					params.put(unprefixed, values[0]);
				}
			}
		}
		return params;
	}

	/**
	 * 对 Http Basic 验证的 Header 进行编码. 这样可以增加安全性(不推荐使用 Basic 的认证).
	 * 
	 * @param userName
	 *            用户名
	 * @param password
	 *            密码
	 * @return
	 */
	public static String encodeHttpBasic(String userName, String password) {
		String encode = userName + ":" + password;
		return "Basic " + EncoderUtil.base64Encode(encode.getBytes());
	}
	
	/**
	 * 获取某次请求的完整的 url, 比如在登录前进行记录该 url, 登录成功后返回该页面. 形如:
	 * http://www.mysterylab.org/index.do?method=xxx&productId=yyy
	 * 
	 * @param request
	 * @return
	 */
	public static String getFullRequestUrl(HttpServletRequest request) {
		
		StringBuffer sb = request.getRequestURL();
		int count = 0;
		Enumeration<String> paramNames = request.getParameterNames();
		while (paramNames != null && paramNames.hasMoreElements()) {
			count++;
			String paramName = paramNames.nextElement();
			String paramValue = request.getParameter(paramName);
			if (1 == count) {
				sb.append("?");
			} else {
				sb.append("&");
			}
			sb.append(paramName);
			sb.append("=");
			sb.append(paramValue);
		}
		return sb.toString();
	}
}
